package cn.kgc.service.impl;

import cn.kgc.client.RestDmImageClient;
import cn.kgc.client.RestDmUserClient;
import cn.kgc.dto.Dto;
import cn.kgc.dto.DtoUtil;
import cn.kgc.exception.UserErrorCode;
import cn.kgc.pojo.DmImage;
import cn.kgc.pojo.DmUser;
import cn.kgc.service.UserService;
import cn.kgc.util.RedisUtil;
import cn.kgc.utils.*;
import cn.kgc.vo.DmUserVo;
import cn.kgc.vo.TokenVo;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import javax.annotation.Resource;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.concurrent.ExecutionException;

@Service
public class UserServiceImpl implements UserService {

    @Resource
    RestDmUserClient restDmUserClient;

    @Resource
    RestDmImageClient restDmImageClient;

    @Resource
    RedisUtil redisUtil;

    @Resource
    SmsUtils smsUtils;

    /**
     * 思路
     * 业务实现逻辑
     * 登录的思路
     * 1.用户名和密码是否正确，根据用户名(phone）获取用户对象，判断密码是否正确,User
     * 2.用户名，密码校验成功，就可以获取一个userVo,把User(pojo)导入到UserVo ,组装数据
     * 3.获取用户头像信息，组装到userVo中
     * 4.生成一个token,是一个字符串，令牌
     * 5.把token放到redis中储存起来
     * 6.做一个tokenVo ,封装token,生成时间，过期时间
     * 7.tokenvo和userVo封装Object[]数组的方式返回
     *
     * @param phone
     * @param
     * @return
     */
    @Override
    public Object[] queryLogin(String phone, String secretPassword) throws Exception {

        //      1.用户名和密码是否正确，根据用户名(phone）获取用户对象，判断密码是否正确,User
        Map map = new HashMap();

        map.put("phone", phone);
        List<DmUser> listByMap = restDmUserClient.getDmUserListByMap(map);
        //如果为空或者查出来的数据返回结果为0
        if (listByMap == null || listByMap.size() == 0) {
            throw new BaseException(UserErrorCode.User_No_EXIST);
        }
        DmUser dmUser = listByMap.get(0);
        //判断密码是否为空或者密码是否和数据库密码相等
        if (EmptyUtils.isEmpty(secretPassword) || !(dmUser.getPassword().equals(secretPassword))) {
            throw new BaseException(UserErrorCode.User_ERROR_PASSWORD);
        }

        //      2.用户名，密码校验成功，就可以获取一个userVo,把User(pojo)导入到UserVo ,组装数据
        DmUserVo dmuserVo = new DmUserVo();
        BeanUtils.copyProperties(dmUser, dmuserVo);
        dmuserVo.setUserId(dmUser.getId());

        //      3.获取用户头像信息，组装到userVo中
        List<DmImage> dmImage_defaultImage = restDmImageClient.getDmImage_DefaultImage(dmUser.getId(),
                Constants.Image.ImageType.normal,
                Constants.Image.ImageCategory.user);

        dmuserVo.setImageId(dmImage_defaultImage.get(0).getId());
        dmuserVo.setImgUrl(dmImage_defaultImage.get(0).getImgUrl());

        //      4.生成一个token,是一个字符串，令牌
        String token = generateToken(phone);

        //      5.把token放到redis中储存起来
        saveToken(token, dmuserVo);

        //      6.做一个tokenVo ,封装token,生成时间，过期时间
        TokenVo tokenVo = new TokenVo();
        Date date = new Date();

        //把token放进tokenVo中，设置用户认证凭据
        tokenVo.setToken(token);
        //设置有效时长
        tokenVo.setExpTime(date.getTime() + Constants.Redis_Expire.SESSION_TIMEOUT * 1000);
        //设置生成时间
        tokenVo.setGenTime(date.getTime());

        //7.tokenvo和userVo封装Object[]数组的方式返回
        return new Object[]{dmuserVo, tokenVo};
    }

    //保存Token
    public void saveToken(String token, DmUserVo dmuserVo) {
        //1.token放到redis里面存储， 将来登录校验验证  key(?)  value（token）
        String key = Constants.USER_TOKEN_PREFIX + dmuserVo.getPhone();
        //如果redisUtil.get（key)获取key不为空
        if (!EmptyUtils.isNotEmpty(redisUtil.get(key)))
            //删除缓存
            redisUtil.del(key);
        //普通缓存放入并设置到期时间
        redisUtil.set(key, token, Constants.Redis_Expire.SESSION_TIMEOUT);
        //2.dmUserVo存到redis中， 将来根据token获取用户信息 key(token) value(userVo)
        redisUtil.set(token, dmuserVo, Constants.Redis_Expire.SESSION_TIMEOUT);

    }


    //生成令牌
    public String generateToken(String wxid_phone) {
        //唯一的
        StringBuffer tokenStringBuffer = new StringBuffer();
        //时间戳，phone,随机数---->唯一
        tokenStringBuffer.append("token-pc");
        //添加MD5加密码
        tokenStringBuffer.append(MD5.getMd5(wxid_phone, 8));
        tokenStringBuffer.append("-");
        //添加系统时间
        tokenStringBuffer.append(new SimpleDateFormat("yyyyMMddHHmmss").format(new Date()));
        tokenStringBuffer.append("-");
        //获取UUID生成的随机数，截取从第1位到第6位
        tokenStringBuffer.append(UUID.randomUUID().toString(), 0, 6);
//        tokenStringBuffer.append(MD5.getRandomCode());
        //返回生产的令牌
        return tokenStringBuffer.toString();
    }

    /**
     * token置换是有条件，有时间要求的，token生成的时间必须是大于1.5h，
     * 也就是说旧的token在redis中剩余的时间小于30分钟，就允许续约。否则不允许置换。
     *
     * @param token
     * @return
     * @throws BaseException
     */

    //Token置换
    @Override
    public TokenVo replaceToken(String token) throws BaseException {
        //1.判断token是否是置换t期，布置置换期不需要置换
        long expire = redisUtil.getExpire(token);
        if (expire == -2) {
            throw new BaseException(UserErrorCode.USER_NOT_LOGIN);
        }
        //redis的token还有超过30分钟有效期，这个token不置换token
        if (expire > Constants.Redis_Expire.REPLACEABLE_TIME_RANGE) {
            throw new BaseException(UserErrorCode.TOKEN_NOT_TIMEOUT);
        }
        //2.获取token的用户的信息
        DmUserVo dmUserVo = (DmUserVo) redisUtil.get(token);

        //3.生成key( "userToken:phone---zhzbin@163.com")，
        // 生成新的token,放到redis中
        //4.生成token  用户信息， 放到redis中
        String newToken = this.generateToken(dmUserVo.getPhone());
        this.saveToken(newToken, dmUserVo);

        //5.新老交替，老token延时死亡，
        // 给2分钟,给新的token有时间返回到浏览器放到浏览器的localStorage(中)
        redisUtil.expire(token, Constants.Redis_Expire.REPLACEMENT_DELAY);

        //6.返回tokenvo
        Date date = new Date();
        TokenVo tokenVo = new TokenVo();
        //把新token放进tokenVo中
        tokenVo.setToken(newToken);
        //设置新token的过期时间
        tokenVo.setExpTime(date.getTime() + Constants.Redis_Expire.SESSION_TIMEOUT * 1000);
        //设置新token的生成时间
        tokenVo.setGenTime(date.getTime());
        return tokenVo;
    }

    //发送登录手机验证码
    @Override
    public void sendLoginCode(String phone) throws BaseException, ExecutionException, InterruptedException {
        //1.从redis中获取phone的失效时间，1分钟，如果存在这个key,
        // 说明在1分钟内，不允许重新发送code
        if (redisUtil.getExpire(Constants.USER_LOGIN_CODE + phone) >
                Constants.Redis_Expire.LOGIN_CODE_DELAY - Constants.Redis_Expire.DEFAULT_EXPIRE) {
            throw new BaseException(UserErrorCode.USER_CODE_EXIST);
        }

        //2.redis中没有这个phone的验证码，可以发送验证码
        //生成随机数
        Double code = (1000 + Math.random() * 9000);
        redisUtil.set(Constants.USER_LOGIN_CODE + phone,
                code.intValue(),
                Constants.Redis_Expire.LOGIN_CODE_DELAY);

        //发送短信
        String sendSmsCode = smsUtils.sendSmsCode(phone, Constants.SMS_Template.LOGIN, "{code:" + code.intValue() + "}");

        //不为空且返回为ok

        if (sendSmsCode != null && sendSmsCode.equals("OK")) {
            //3.验证码放到redis中，并且给过期时间5分钟
            redisUtil.set(Constants.USER_LOGIN_CODE + phone, code.intValue(), Constants.Redis_Expire.LOGIN_CODE_DELAY);
        }
    }

    //手机验证码校验
    @Override
    public Object[]  checkPhoneLoginCode(String phone, String vcode) throws Exception {

        //1.redis phone获取验证码,普通缓存获取
        Object code = redisUtil.get(Constants.USER_LOGIN_CODE + phone);

        //2.不存在验证码，或者验证码不匹配vcode,抛异常
        if (vcode == null && !Objects.equals(vcode, code)) {
            throw new BaseException(UserErrorCode.USER_ERROR_PHONE_CODE);
        }

        //3.通过手机号码查询用户信息,user，如果没有用户信息，就是第一次登录
        Map map = new HashMap<>();
        map.put("phone", phone);
        List<DmUser> dmUserListByMap = restDmUserClient.getDmUserListByMap(map);

        //4.第一次登录，则需要插入phone放入到数据库的user表（注册）,new 一个user,封装phone
        if (CollectionUtils.isEmpty(dmUserListByMap) || dmUserListByMap.size() == 0) {
            DmUser dmUser = new DmUser();
            dmUser.setPhone(phone);
            Integer qdtxAddDmUser = restDmUserClient.qdtxAddDmUser(dmUser);
            //判断用户是否添加成功
            if (qdtxAddDmUser > 0) {
                return new Dto[]{DtoUtil.returnDataSuccess(Constants.DATABASE_OPERATION_SUCCEED)};
            } else {
                throw new BaseException(UserErrorCode.DATABASE_OPERATION_FAILRE);
            }
        }

        DmUser dmUser = dmUserListByMap.get(0);
        DmUserVo dmUserVo = new DmUserVo();
        BeanUtils.copyProperties(dmUser, dmUserVo);
        dmUserVo.setUserId(dmUser.getId());

        //获取用户头像
        List<DmImage> dmImage_defaultImage = restDmImageClient.getDmImage_DefaultImage(dmUser.getId(),
                Constants.Image.ImageType.normal,
                Constants.Image.ImageCategory.user);
        dmUserVo.setImageId(dmImage_defaultImage.get(0).getId());
        dmUserVo.setImgUrl(dmImage_defaultImage.get(0).getImgUrl());


        //5.生成token放到redis,返回tokenvo

        String token = generateToken(phone);
        Date date = new Date();
        TokenVo tokenVo = new TokenVo();
        tokenVo.setToken(token);
        tokenVo.setExpTime(date.getTime() + Constants.Redis_Expire.SESSION_TIMEOUT * 1000);
        //设置生成时间
        tokenVo.setGenTime(date.getTime());

        //6.tokenVo和user放到数组中，返回
        return new Object[]{tokenVo, dmUserVo};
    }


}
